﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Aletfa.LightManager.Effects;

namespace Aletfa.LightManager.Console
{
    internal sealed class ConsoleParser
    {
        public event VoidEventHandler LogClearRequested;
        public event GenericEventHandler<string> LogRequested;

        private GameManager Manager { get; set; }

        public ConsoleParser(GameManager manager)
        {
            this.Manager = manager;
        }

        public void ParseCommand(string text)
        {
            text = text.Replace("\r\n", "");
            try
            {
                Action action = null;

                List<string> splitted = new List<string>(text.Split(' '));

                Unit from = this.GetTarget(splitted[0]);
                if (from != null)
                    splitted.RemoveAt(0);

                switch (splitted[0].ToLower())
                {
                    case "damage":
                        splitted.RemoveAt(0);
                        action = this.ParseDamageCommand(splitted);
                        break;
                    case "heal":
                        splitted.RemoveAt(0);
                        action = this.ParseHealCommand(splitted);
                        break;
                    case "status":
                        splitted.RemoveAt(0);
                        action = this.ParseStatusCommand(splitted);
                        break;
                    case "action":
                        splitted.RemoveAt(0);
                        action = this.ParseActionCommand(splitted);
                        break;
                    case "armor":
                        splitted.RemoveAt(0);
                        action = this.ParseArmorCommand(splitted);
                        break;
                    case "shield":
                        splitted.RemoveAt(0);
                        action = this.ParseShieldCommand(splitted);
                        break;
                    case "edit":
                        splitted.RemoveAt(0);
                        this.ParseEditCommand(splitted);
                        break;
                    case "create":
                        splitted.RemoveAt(0);
                        this.ParseCreateCommand(splitted);
                        break;
                    case "destroy":
                        splitted.RemoveAt(0);
                        this.ParseDestroyCommand(splitted);
                        break;
                    case "board":
                        splitted.RemoveAt(0);
                        this.ParseBoardCommand(splitted);
                        break;
                    case "turn":
                        this.Manager.NextTurn();
                        break;
                    case "victory":
                        this.BattleEnd();
                        break;
                    case "clear":
                        this.LogClearRequested();
                        break;
                    default:
                        throw new ConsoleException(string.Format("\"{0}\"? non capisco.", splitted[0]));
                }
                if (action != null)
                {
                    action.From = (from != null) ? from.Name.ToString() : "";
                    this.Manager.RegisterAction(action);
                    //if (action.Duration > 1)
                    //    this.Log(string.Format("<font class='details'>l'azione su {0} prosegue per {1} turni</font>", action.Target.Name, action.Duration - 1));
                }

            }
            catch (ConsoleException ex)
            {
                this.LogRequested(string.Format("<font class='error'>{0}</font>", ex.Message));
            }
            catch (Exception)
            {
                this.LogRequested(string.Format("<font class='error'>qualcosa non va...</font>"));
            }
        }

        private Unit GetTarget(string target)
        {
            return this.Manager.Units.FirstOrDefault(u => u.Name.ToLower() == target.ToLower());
        }

        private Unit GetTarget(string target, string operation)
        {
            Unit result = this.GetTarget(target);
            if (result == null)
                throw new ConsoleException(string.Format("{0}: non capisco il bersaglio", operation));
            return result;
        }

        private int GetDuration(List<string> command)
        {
            for (int i = command.Count - 1; i >= 0; i--)
            {
                string arg = command[i];
                if (!arg.StartsWith("x")) continue;
                char[] chars = arg.ToCharArray();
                if (chars.Length < 2) return 0;
                if (chars[1] < '1' || chars[1] > '9') return 0;
                string duration = arg.Substring(1, arg.Length - 1);
                command.RemoveAt(i);
                return Convert.ToInt32(duration);
            }
            return 0;
        }

        public AlterEffectType GetAlterEffect(string effect, string operation)
        {
            switch (effect.ToLower())
            {
                case "add":
                    return AlterEffectType.Add;
                case "remove":
                    return AlterEffectType.Remove;
                case "reset":
                    return AlterEffectType.Reset;
                default:
                    throw new ConsoleException(string.Format("{0}: è ammesso solo add, remove e reset", operation));
            }
        }

        private void BattleEnd()
        {
            MessageBoxResult result = MessageBox.Show("Il comando completa la battaglia.\r\nGli antagonisti e gli effetti relativi alla battaglia saranno rimossi.", "Battle over", MessageBoxButton.OKCancel, MessageBoxImage.Question);

            if (result != MessageBoxResult.OK) return;
            this.Manager.EndFight();
        }

        private void ParseDestroyCommand(List<string> command)
        {
            Unit target = this.GetTarget(command[0], "destroy");
            MessageBoxResult result = MessageBox.Show(string.Format("Rimuovere la pedina \"{0}\"?", target.Name), "Unit destroy", MessageBoxButton.OKCancel, MessageBoxImage.Question);
            if (result != MessageBoxResult.OK) return;
            this.Manager.RemoveUnit(target);
        }

        private Action ParseDamageCommand(List<string> command)
        {
            DamageEffect damage = new DamageEffect();
            Action result = new Action(damage);

            result.Duration = this.GetDuration(command);
            //if (result.Duration > 0)
            //    result.Duration--;

            //niente armatura
            for (int i = command.Count - 1; i >= 0; i--)
            {
                string arg = command[i];
                if (arg.ToLower() != "no") continue;
                damage.IgnoreArmor = true;
                command.RemoveAt(i);
            }

            result.Target = this.GetTarget(command[0], "damage");

            for (int i = 1; i < command.Count; i++)
            {
                string dmg = command[i];
                if (dmg.StartsWith("x")) continue;
                bool aggravated = false;
                if (dmg.Contains('a'))
                {
                    aggravated = true;
                    dmg = dmg.Substring(0, dmg.Length - 1);
                }
                DamageSources source = DamageSources.Phisical;
                if (i + 1 < command.Count)
                    if (command[i + 1].ToCharArray()[0] > '9')
                    {
                        Enum.TryParse<DamageSources>(command[i + 1], true, out source);
                        i++;
                    }

                damage.Damages.Add(new Damage() { Number = Convert.ToInt32(dmg), Soruce = source, Aggravate = aggravated });
            }

            return result;
        }

        private Action ParseHealCommand(List<string> command)
        {
            HealingEffect heal = new HealingEffect();
            Action result = new Action(heal);

            result.Duration = this.GetDuration(command);
            //if (result.Duration > 0)
            //    result.Duration--;

            result.Target = this.GetTarget(command[0], "heal");

            string points = command[1];
            bool aggravated = false;
            if (points.Contains('a'))
            {
                aggravated = true;
                points = points.Substring(0, points.Length - 1);
            }

            heal.Aggravated = aggravated;
            heal.Number = Convert.ToInt32(points);

            return result;
        }

        private Action ParseArmorCommand(List<string> command)
        {
            ArmorEffect armor = new ArmorEffect();
            Action result = new Action(armor);

            result.Duration = this.GetDuration(command);
            armor.IsTemporary = (result.Duration > 0);

            result.Target = this.GetTarget(command[0], "armor");

            armor.Type = this.GetAlterEffect(command[1], "armor");

            string points = command[2];
            armor.Ammount = Convert.ToInt32(points);

            DamageSources source = DamageSources.Phisical;
            if (command.Count > 3)
                if (command[3].ToCharArray()[0] > '9')
                    Enum.TryParse<DamageSources>(command[3], true, out source);
            armor.Source = source;

            return result;
        }

        private Action ParseShieldCommand(List<string> command)
        {
            ShieldEffect shield = new ShieldEffect();
            Action result = new Action(shield);

            result.Duration = this.GetDuration(command);
            shield.IsTemporary = (result.Duration > 0);

            result.Target = this.GetTarget(command[0], "armor");

            shield.Type = this.GetAlterEffect(command[1], "armor");

            string points = command[2];
            shield.Ammount = Convert.ToInt32(points);

            DamageSources source = DamageSources.Phisical;
            if (command.Count > 3)
                if (command[3].ToCharArray()[0] > '9')
                    Enum.TryParse<DamageSources>(command[3], true, out source);
            shield.Source = source;

            return result;
        }

        private Action ParseStatusCommand(List<string> command)
        {
            CustomStatusEffect status = new CustomStatusEffect();
            Action result = new Action(status);

            result.Duration = this.GetDuration(command);
            if (result.Duration > 0)
                status.IsTemporary = true;

            result.Target = this.GetTarget(command[0], "status");
            command.RemoveAt(0);

            status.Type = this.GetAlterEffect(command[0], "action");
            command.RemoveAt(0);

            if (status.Type != AlterEffectType.Reset)
            {
                command.ForEach(s => status.Description += s + " ");
                status.Description = status.Description.Substring(0, status.Description.Length - 1);
            }
            return result;
        }

        private Action ParseActionCommand(List<string> command)
        {
            ActionEffect combatAction = new ActionEffect();
            Action result = new Action(combatAction);

            result.Target = this.GetTarget(command[0], "action");

            combatAction.Type = this.GetAlterEffect(command[1], "action");

            return result;
        }

        private void ParseBoardCommand(List<string> command)
        {
            if (command.Count == 0)
                this.Manager.RequestBoardSetup(false);

            if (command.Count == 1)
            {
                if (command[0] == "setup")
                    this.Manager.RequestBoardSetup(true);
            }
        }

        private void ParseCreateCommand(List<string> command)
        {
            Unit test = new Unit() { Name = command[0], IsEnemy = false };
            test.Initialize(20);
            this.Manager.Units.Add(test);
        }

        private void ParseEditCommand(List<string> command)
        {
            Unit target = this.GetTarget(command[0], "edit");
            EditorWindow editor = new EditorWindow();
            editor.NameCheckRequested += editor_NameCheckRequested;
            editor.UnitSaved += editor_UnitSaved;
            editor.DataSource = target;
            editor.ShowDialog();
        }

        void editor_NameCheckRequested(Editor.ValidationArgs arg)
        {
            arg.IsValid = !this.Manager.Units.Any(u => u.Name.ToLower() == arg.Subject.ToLower());
        }

        void editor_UnitSaved(Unit arg)
        {
            this.Manager.NotifyUnitChanged(arg);
        }

    }
}
